﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Windows.Navigation;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using WindowsPhoneEssentials.Abstractions;
using WindowsPhoneEssentials.Testing.Abstractions;
using WindowsPhoneMVC.Framework;
using WindowsPhoneMVC.Navigation;
using WindowsPhoneMVC.Tests.Navigation.Fakes;
using WindowsPhoneMVC.Tests.Phone.Fakes;

namespace WindowsPhoneMVC.Tests.Framework
{
    [TestClass]
    public class NavigationApplicationActivatorTests
    {
        private PhoenixBootstrapper activator;
        private FakePhoneApplicationFrame testPhoneApplicationFrame;
        private FakeMvcFrame fakeFrame;
        private FakeNavigator fakeNavigator;
        private FakePhoenixApplication fakePhoenixApplication;
        private IPhoneApplicationService phoneApplicationService;
        private FakeJournal journal;

        [TestInitialize]
        public void Setup()
        {
            fakeFrame = new FakeMvcFrame();
            testPhoneApplicationFrame = new FakePhoneApplicationFrame();
            fakeNavigator = new FakeNavigator();
            fakePhoenixApplication = new FakePhoenixApplication();
            phoneApplicationService = new FakePhoneApplicationService();
            journal = new FakeJournal();
            activator = new PhoenixBootstrapper(fakeNavigator, fakePhoenixApplication, 
                testPhoneApplicationFrame, phoneApplicationService, n => {}, journal, "testcontroller.searchextraaction");
        }

        [TestMethod]
        public void should_not_initialise_if_not_navigating_to_shell()
        {
            // arrange
            var fakeShell = new FakeShell(fakeFrame);
            var navigatorInitialisationCount = 0;
            fakeNavigator.InitialiseAction = (j, f, n) => { navigatorInitialisationCount++; };

            // act
            activator.NavigationAppStartup(new NavigationEventArgs(fakeShell, new Uri("/")));

            // assert
            Assert.AreEqual(0, navigatorInitialisationCount);
        }

        [TestMethod]
        public void navigation_application_initialises_navigators_on_initial_navigation()
        {
            // arrange
            var fakeShell = new FakeShell(fakeFrame);
            var navigatorInitialisationCount = 0;
            fakeNavigator.InitialiseAction = (j, f, n) => { navigatorInitialisationCount++; };

            // act
            activator.NavigationAppStartup(new NavigationEventArgs(fakeShell, new Uri("/Shell.xaml", UriKind.Relative)));

            // assert
            Assert.AreEqual(1, navigatorInitialisationCount);
        }

        [TestMethod]
        public void navigation_application_only_initialises_navigators_once()
        {
            // arrange
            var fakeShell = new FakeShell(fakeFrame);
            var navigatorInitialisationCount = 0;
            fakeNavigator.InitialiseAction = (j, f, n) => { navigatorInitialisationCount++; };

            // act
            activator.NavigationAppStartup(new NavigationEventArgs(fakeShell, new Uri("/Shell.xaml", UriKind.Relative)));
            activator.NavigationAppStartup(new NavigationEventArgs(fakeShell, new Uri("/Shell.xaml", UriKind.Relative)));

            // assert
            Assert.AreEqual(1, navigatorInitialisationCount);
        }

        [TestMethod]
        public void shell_initialised_on_cold_start()
        {
            // arrange
            var navigatorInitialised = false;
            fakeNavigator.InitialiseAction = (j, f, n) => navigatorInitialised = true;

            // act
            activator.NavigationAppStartup(new NavigationEventArgs(new FakeShell(fakeFrame), new Uri("/Shell.xaml", UriKind.Relative)));

            // assert
            Assert.IsNotNull(activator.InitializedShell);
            Assert.IsTrue(navigatorInitialised);
        }

        [TestMethod]
        public void does_not_reinitialise_after_dormant()
        {
            // arrange
            var navigatorInitialised = false;
            fakeNavigator.InitialiseAction = (j, f, n) => navigatorInitialised = true;
            var navigationEventArgs = new NavigationEventArgs(new FakeShell(fakeFrame), new Uri("/Shell.xaml", UriKind.Relative));
            activator.NavigationAppStartup(navigationEventArgs); //sets initialised up to simulate dormant and reactivate
            navigatorInitialised = false;
            activator.InitializingAfterTombstoning = true;

            // act
            activator.NavigationAppStartup(navigationEventArgs);

            // assert
            Assert.IsFalse(navigatorInitialised);
        }

        [TestMethod]
        public void reinitialises_after_tombstone()
        {
            // arrange
            journal.PushEntry(new NavigationRequest());
            var navigatorInitialised = false;
            fakeNavigator.InitialiseAction = (j, f, n) => navigatorInitialised = true;
            activator.InitializingAfterTombstoning = true;

            // act
            activator.NavigationAppStartup(new NavigationEventArgs(new FakeShell(fakeFrame), new Uri("/Shell.xaml", UriKind.Relative)));

            // assert
            Assert.IsTrue(navigatorInitialised);
        }

        [TestMethod]
        public void pops_and_navigates_to_last_action_on_tombstone_restore()
        {
            // arrange
            activator.InitializingAfterTombstoning = true;
            journal.PushEntry(new NavigationRequest("TestFrame", "TestController", "TestAction"));
            fakePhoenixApplication.FrameName = "TestFrame";

            // act
            activator.NavigationAppStartup(new NavigationEventArgs(new FakeShell(fakeFrame), new Uri("/Shell.xaml", UriKind.Relative)));

            // assert
            Assert.AreEqual("TestController", fakeNavigator.CurrentController);
            Assert.AreEqual("TestAction", fakeNavigator.CurrentAction);
        }

        [TestMethod]
        public void navigates_to_deeplink_link_after_initialisation()
        {
            // arrange
            var uri = new Uri(DeepLink.UriFor("testcontroller", "testaction", new Dictionary<string, string>()), UriKind.Relative);

            // act
            activator.NavigationAppStartup(new NavigationEventArgs(new FakeShell(fakeFrame), uri));

            // assert
            Assert.AreEqual("testcontroller", fakeNavigator.CurrentController);
            Assert.AreEqual("testaction", fakeNavigator.CurrentAction);
        }

        [TestMethod]
        public void cancels_forward_navigation_in_progress_on_backkey_press()
        {
            // arrange
            journal.PushEntry(new NavigationRequest());
            fakeNavigator.Initialise(journal, fakeFrame, "TestFrame");
            var cancelled = false;
            fakeNavigator.NavigationCancelled += (sender, e) => cancelled = true;
            var fakeShell = new FakeShell(fakeFrame);
            activator.NavigationAppStartup(new NavigationEventArgs(fakeShell, new Uri("/Shell.xaml", UriKind.Relative)));
            fakeFrame.IsNavigationInProgress = true;
            fakeFrame.NavigationDirection = NavigationDirection.Forward;

            // act
            var cancelEventArgs = new CancelEventArgs();
            fakeShell.OnBackKeyPress(cancelEventArgs);

            // assert
            Assert.IsTrue(cancelled);
            Assert.IsTrue(cancelEventArgs.Cancel);
        }

        [TestMethod]
        public void backkey_press_during_backwards_navigation_navigates_backwards_again()
        {
            // arrange
            var fakeShell = new FakeShell(fakeFrame);
            activator.NavigationAppStartup(new NavigationEventArgs(fakeShell, new Uri("/Shell.xaml", UriKind.Relative)));
            journal.PushEntry(new NavigationRequest());
            fakeFrame.IsNavigationInProgress = true;
            fakeFrame.NavigationDirection = NavigationDirection.Backward;

            // act
            var cancelEventArgs = new CancelEventArgs();
            fakeShell.OnBackKeyPress(cancelEventArgs);

            // assert
            Assert.AreEqual(0, journal.Requests.Count);
            Assert.IsTrue(cancelEventArgs.Cancel);
        }

        [TestMethod]
        public void backkey_press_during_initial_navigation_will_not_handle_backkey_press()
        {
            // arrange
            var fakeShell = new FakeShell(fakeFrame);
            activator.NavigationAppStartup(new NavigationEventArgs(fakeShell, new Uri("/Shell.xaml", UriKind.Relative)));
            fakeFrame.IsNavigationInProgress = true;
            fakeFrame.NavigationDirection = NavigationDirection.Forward;

            // act
            var cancelEventArgs = new CancelEventArgs();
            fakeShell.OnBackKeyPress(cancelEventArgs);

            // assert
            Assert.IsFalse(cancelEventArgs.Cancel);
        }

        [TestMethod]
        public void navigates_to_search_extras_link_after_initialisation()
        {
            // arrange
            var uri = new Uri("/SearchExtras?some=args", UriKind.Relative);

            // act
            activator.NavigationAppStartup(new NavigationEventArgs(new FakeShell(fakeFrame), uri));

            // assert
            Assert.AreEqual("testcontroller", fakeNavigator.CurrentController);
            Assert.AreEqual("searchextraaction", fakeNavigator.CurrentAction);
        }
    }
}